#include<stdlib.h>
#include<time.h>
#include"chromo.h"

// The Chromo class de/constructors

Chromo::Chromo()
{
  size = 0;
  seq = NULL;
  fit = 0.0;
}

// This function need some error handling code for a negative chromo_size input

Chromo::Chromo(int chromo_length, bool init)
{
  size = chromo_length;
  if (size != 0)
    seq = new int[size];
  else
    seq = NULL;
  fit = 0.0;
  modified = true;
  if (init)
    randseq();
}

Chromo::Chromo(const Chromo &copy)
{
  size = copy.size;
  if(size != 0)
    {
      seq = new int[size];
      int i;
      for (i = 0; i < size; i++)
	seq[i] = copy.seq[i];
    }
  else
    seq = NULL;
  fit = copy.fit;
  modified = copy.modified;
}

Chromo::~Chromo()
{
  if (seq)
    {
      delete[] seq;
      seq = NULL;
    }

}

void Chromo::re_setup(int chromo_length, bool init)
{
  size = chromo_length;

  if (seq) // seq != NULL
    delete[] seq;
  if (size != 0)
    seq = new int[size];
  else
    seq = NULL;
  fit = 0.0;
  modified = true;
  if (init)
    randseq();
}

Chromo& Chromo::operator=(const Chromo &copy)
{
  if (this == &copy)
    return *this;

  if (size != copy.size)
    {
      if (seq) // seq != NULL
	delete[] seq;

      size = copy.size;
      if (size == 0)
	{
	  seq = NULL;
	  return *this;
	}
      else
	{
	  seq = new int[size];
	  int i;
	  for (i = 0; i < size; i++)
	    seq[i] = copy.seq[i];
	}
    }
  else
    {
      int i;
      for (i = 0; i < size; i++)
	seq[i] = copy.seq[i];
    }

  fit = copy.fit;
  modified = copy.modified;
  return *this;
}

bool Chromo::operator<(const Chromo &t) const
{
  return fit < t.fit;
}

bool Chromo::operator>(const Chromo &t) const
{
  return fit > t.fit;
}

ostream &operator<<(ostream &os, const Chromo &t)
{
  int i;

  if (t.seq == NULL)
    os << "NULL chromosome";
  else
    {
      for (i = 0; i < t.size; i++)
	os << t.seq[i];
      os << '\t' << t.fit;
    }
  return os;
}
	


double Chromo::fitness(double fitness)
{
  if (fitness != -DBL_MAX)
    {
      fit = fitness;
      modified = false;
    }

  return fit;
}

int Chromo::bit_mutate(int position)
{
  if (position < 0)
    return - 1;

  seq[position] ^= 1;
  modified = true;
  return position;
}

void Chromo::rand_mutate(double rate)
{
  if (rate < 0 || rate > 1.0)
    return;

  int i;
  double temp;
  for (i = 0; i < size; i++)
    {
      temp = double(rand())/RAND_MAX;
      if (temp < rate)
	seq[i] ^= 1;
    }
  modified = true;
}

int Chromo::crossover(Chromo &partner, int pos1, int pos2)
{
  if (pos2 == 0)
    pos2 = size - 1;

  if (pos2 < pos1 || pos1 < 0)
    return -1; 

  int i;
  for (i = pos1; i <= pos2; i++)
    {
      seq[i] ^= partner.seq[i];
      partner.seq[i] ^= seq[i];
      seq[i] ^= partner.seq[i];
    }
  modified = true;
  return pos2 - pos1 + 1;
}

int Chromo::loci(int index, int value)
{
  if (value != -1)
    seq[index] = value;

  return seq[index];
}
int Chromo::randseq()
{
  if (seq == NULL)
    return -1;



  int i;

  // the method used to generate random binary sequence is from the website:
  // http://c-faq.com/lib/randrange.html
  for (i = 0; i < size; i++)
    seq[i] = (int)((double)rand() / ((double)RAND_MAX + 1) * 2);

  return size;
}


inline unsigned long power2(unsigned int pow)
{
  long result = 1;
  int i;
  for (i = 0; i < pow; i++)
    result *= 2;

  return result;
}


unsigned long Chromo::debin() const
{

 if (seq == NULL)
    return 0;

  int i;
  long result = 0;
  for (i = 0; i < size; i++)
    result += seq[i]*power2(i);

  return result;
}

int Chromo::ham_dist(const Chromo &target) const
{
  if (size != target.size || seq == NULL)
    return -1;

  int i;
  int counter = 0;
  for (i = 0; i < size; i++)
    if (seq[i] != target.seq[i])
      counter++;

  return counter;
}

bool Chromo::if_modified()
{
  return modified;
}
